//{=============================================================================
//! @file      The_Set.c
//! @date      2014-04-17 22:01:24 +0400
//! @author    Roman Anufriev <mrspeedstalker@gmail.com>
//!
//! @brief     .c file that contains description of the functions declared in
//!            The_Set.h library.
//}=============================================================================


#include "The_Set.h"

// private:
// function for creating theSet_elem from data that user want to store in theSet
struct theSet_elem* theSet_create_elem (void*  new_elem_t_ptr,
                                        size_t sizeof_new_elem_t)
	{
	struct theSet_elem* new_elem_ptr = NULL;
	
	if (new_elem_t_ptr)
		{
		// allocating memory for theSet_elem
		new_elem_ptr = (struct theSet_elem*) calloc (1, sizeof (struct theSet_elem));
		assert (new_elem_ptr);
		
		// allocating memory for data given by user
		new_elem_ptr -> elem_t = calloc (1, sizeof_new_elem_t);
		assert (new_elem_ptr -> elem_t);
		// copying data given by user to new theSet_elem
		memcpy (new_elem_ptr -> elem_t, new_elem_t_ptr, sizeof_new_elem_t);
		new_elem_ptr -> next = NULL;
		
		return new_elem_ptr;
		}
	else
		return NULL;
	}

// private:
// function for deleting theSet_elem created from data that user want to store in theSet
enum boolean theSet_delete_elem (struct theSet_elem* elem_to_del)
	{
	if (elem_to_del)
		{
		free (elem_to_del -> elem_t); elem_to_del -> elem_t = NULL;
		free (elem_to_del          ); elem_to_del           = NULL;
		return true;
		}
	else
		return false;
	}

// /\-/\-/\-/\-/\-/\-/\-/\-/\-/\-/\-/\-/\-/\-/\-/\-/\-/\-/\-/\-/\-/\-/\-/\-/\-/\
// \/-\/-\/-\/-\/-\/-\/-\/-\/-\/-\/-\/-\/-\/-\/-\/-\/-\/-\/-\/-\/-\/-\/-\/-\/-\/

void theSet_construct (struct theSet_elem** theSet_head)
	{
	// just zeroing of the pointer
	assert (theSet_head);
	*theSet_head = NULL;
	}

void theSet_destruct  (struct theSet_elem** theSet_head)
	{
	enum boolean ret_val = false;
	
	struct theSet_elem *curr = NULL,
	                   *prev = NULL;
	
	assert (theSet_head);
	
	// if (*theSet_head = NULL), I consider it as theSet is already destructed
	if (*theSet_head)
		{
		// detecting that there are only one elem in theSet
		while ( (*theSet_head) -> next )
			{
			curr = *theSet_head;
			
			// selecting the last theSet_elem in theSet
			while (curr -> next)
				{
				prev = curr;
				curr = curr -> next;
				}
			
			// deleting the last theSet_elem in theSet
			ret_val = theSet_delete_elem (curr);
			assert (ret_val);
			
			prev -> next = NULL;
			
			curr = NULL;
			prev = NULL;
			}
			
		// deleting the very last theSet_elem, theSet_head actually
		ret_val = theSet_delete_elem (*theSet_head);
		assert (ret_val);
		}
	}

// /\-/\-/\-/\-/\-/\-/\-/\-/\-/\-/\-/\-/\-/\-/\-/\-/\-/\-/\-/\-/\-/\-/\-/\-/\-/\
// \/-\/-\/-\/-\/-\/-\/-\/-\/-\/-\/-\/-\/-\/-\/-\/-\/-\/-\/-\/-\/-\/-\/-\/-\/-\/

enum boolean theSet_add (struct theSet_elem** theSet_head,
						 void*                new_elem_t,
						 size_t               sizeof_new_elem_t,
						 int    (*compare)(const void* p1, const void* p2))
	{
	enum boolean ret_val = false;
	int compare_res = 0;
	struct theSet_elem *curr     = NULL,
				       *prev     = NULL,
					   *new_elem = NULL;
					   
	assert (theSet_head);
	
	// create theSet_elem from the data given by user
	new_elem = theSet_create_elem (new_elem_t, sizeof_new_elem_t);
	assert (new_elem);
	
	// if it is the first theSet_elem
	if ( !(*theSet_head) && new_elem && compare )
		{
		*theSet_head = new_elem;
		new_elem -> next = NULL;
		return true;
		}
	
	// if it is not the first theSet_elem
	if (*theSet_head && new_elem && compare)
		{
		curr = *theSet_head;
		prev = curr;
		
		while (curr)
			{
			compare_res = compare (curr -> elem_t, new_elem -> elem_t);
			
			// if the same theSet_elem already exists in theSet
			if (compare_res == 0)
				{
				ret_val = theSet_delete_elem (new_elem);
				assert (ret_val);
				return false;
				}
			
			// if curr theSet_elem greater than theSet_elem created from user data
			if (compare_res >  0)
				{
				if ( !((*theSet_head) -> next) )
					{
					new_elem -> next = curr;
					(*theSet_head)   = new_elem;
					}
				else
					{
					if (curr == prev) // if new_elem should be the first elem
						{
						*theSet_head     = new_elem;
						new_elem -> next = curr;
						}
					else
						{
						new_elem -> next = curr;
						prev     -> next = new_elem;
						}
					}
				
				return true;
				}
				
			prev = curr;
			curr = curr -> next;
			}
		
		// if (compare_res <  0) in all comparisons
		prev     -> next = new_elem;
		new_elem -> next = NULL;
		return true;
		}
	
	ret_val = theSet_delete_elem (new_elem);
	assert (ret_val);
	return false;
	}

enum boolean theSet_delete (struct theSet_elem** theSet_head,
							void*                elem_t_to_del,
						    size_t               sizeof_elem_t_to_del,
						    int    (*compare)(const void* p1, const void* p2))
	{
	enum boolean ret_val = false;
	int compare_res = 0;
	struct theSet_elem *curr        = NULL,
	                   *prev        = NULL,
					   *elem_to_del = NULL;
					   
	assert (theSet_head);
	
	// create temporary theSet_elem from the data given by user
	elem_to_del = theSet_create_elem (elem_t_to_del, sizeof_elem_t_to_del);
	assert (elem_to_del);
	
	if ( *theSet_head && elem_to_del && compare )
		{
		curr = *theSet_head;
		prev = curr;
		
		while (curr)
			{
			compare_res = compare (curr -> elem_t, elem_to_del -> elem_t);
			if (compare_res == 0)
				{
				// if it is the first element:
				if ( (prev == (*theSet_head)) && (curr == (*theSet_head)) )
					{
					// if only one elem in theSet, i.e. theSet_head
					if ( !((*theSet_head) -> next) )
						(*theSet_head) = NULL;
					else
						(*theSet_head) = (*theSet_head) -> next;
					}
				else
					prev -> next   = curr -> next; // = NULL if curr = last elem
				
				// deallocating the memory
				ret_val = theSet_delete_elem (curr);
				assert (ret_val);
				
				ret_val = theSet_delete_elem (elem_to_del);
				assert (ret_val);
				return true;
				}
				
			prev = curr;
			curr = curr -> next;
			}
		}
	
	ret_val = theSet_delete_elem (elem_to_del);
	assert (ret_val);
	return false;
	}

// /\-/\-/\-/\-/\-/\-/\-/\-/\-/\-/\-/\-/\-/\-/\-/\-/\-/\-/\-/\-/\-/\-/\-/\-/\-/\
// \/-\/-\/-\/-\/-\/-\/-\/-\/-\/-\/-\/-\/-\/-\/-\/-\/-\/-\/-\/-\/-\/-\/-\/-\/-\/
